#include <string>
#include <sstream>
#include <vector>
#include <math.h>
#include <iomanip>
#include <cstring>

#include "util.h"
#include "sha1.h"
#include "mp2_types.h"

using namespace std;
using namespace mp2;


// Helper function given by class to return result as string displaying 
// relevant information
string get_GET_TABLE_result_as_string(
        const vector<Node>& finger_table,
        const uint32_t m,
        const uint32_t myid,
        const uint32_t idx_of_entry1,
        const std::map<int32_t, File>& keys_table) {
    return get_finger_table_as_string(finger_table, m, myid, idx_of_entry1) + \
           get_keys_table_as_string(keys_table);
}

string get_finger_table_as_string(
        const std::vector<Node>& table,
        const uint32_t m,
        const uint32_t myid,
        const uint32_t idx_of_entry1) {
    std::stringstream s;
    assert(table.size() == (idx_of_entry1 + m));
    s << "finger table:\n";
    for (size_t i = 1; (i - 1 + idx_of_entry1) < table.size(); ++i) {
        using std::setw;
        s << "entry: i= " << setw(2) << i << ", interval=["
            << setw(4) << (myid + (int)pow(2, i-1)) % ((int)pow(2, m))
            << ",   "
            << setw(4) << (myid + (int)pow(2, i)) % ((int)pow(2, m))
            << "),   node= "
            << setw(4) << table.at(i - 1 + idx_of_entry1).hash
            << "\n";
    }
    return s.str();
}

// Finds table of keys
string get_keys_table_as_string(const std::map<int32_t,File>& table) {
    std::stringstream s;
    std::map<int32_t,File>::const_iterator it = table.begin();
    /* std::map keeps the keys sorted, so our iteration will be in
     * ascending order of the keys
     */
    s << "keys table:\n";
    for (; it != table.end(); ++it) {
        using std::setw;
        /* assuming file names are <= 10 chars long */
        s << "entry: k= " << setw(4) << it->first
          << ",  fname= " << setw(10) << it->second.name
          << ",  fdata= " << it->second.data
          << "\n";
    }
    return s.str();
}

// Provided helper function to display info on ADD_FILE
string get_ADD_FILE_result_as_string(
        const char *fname,
        const int32_t key,
        const int32_t nodeId) {
    std::stringstream s;
    s << "fname= " << fname << "\n";
    s << "key= " << key << "\n";
    s << "added to node= " << nodeId << "\n";
    return s.str();
}

// Provided helper function to display info on DEL_FILE
string get_DEL_FILE_result_as_string(
        const char *fname,
        const int32_t key,
        const bool deleted,
        const int32_t nodeId) {
    std::stringstream s;
    s << "fname= " << fname << "\n";
    s << "key= " << key << "\n";
    if (deleted) {
        // then nodeId is meaningful
        s << "was stored at node= " << nodeId << "\n";
        s << "deleted\n";
    }
    else {
        // assume that this means file was not found
        s << "file not found\n";
    }
    return s.str();
}

// Provided helper function to display info on GET_FILE
string get_GET_FILE_result_as_string(
        const char *fname,
        const int32_t key,
        const bool found,
        const int32_t nodeId,
        const char *fdata) {
    std::stringstream s;
    s << "fname= " << fname << "\n";
    s << "key= " << key << "\n";
    if (found) {
        // then nodeId is meaningful
        s << "stored at node= " << nodeId << "\n";
        s << "fdata= " << fdata << "\n";
    }
    else {
        // assume that this means file was not found
        s << "file not found\n";
    }
    return s.str();
}

// Given a string, find the hash value
// Taken from hash-example code
int hashValue(const char* value, int m){
    SHA1Context sha;

    SHA1Reset(&sha);
    SHA1Input(&sha, (unsigned char*) value, strlen(value));

    if (!SHA1Result(&sha)) {
        fprintf(stderr, "key_gen_test: could not compute key ID for %s\n", value);
    }

    return sha.Message_Digest[4]%((int)pow(2,m)) ;
}
